<template>
  <el-container>
    <el-aside width="200px">
      <el-tree
        :data="treeData"
        default-expand-all
        @current-change="treeCurrentChange"
        :props="{ class: customNodeClass }"
      />
    </el-aside>
    <el-container>
      <el-main>
        <RelationGraph
          ref="graphRef$"
          :options="options"
          @contextmenu="handleRightClick"
          @node-click="handleNodeClick"
        >
          <template #node="{ node }">
            <div
              :style="getNodeInlineStyle(node.id)"
              class="graph-node-container"
              :data-id="node.id"
              :class="node.nodeShape == 1 ? 'is-nodeShape' : ''"
              :data-node="JSON.stringify({ id: node.id, data: node.data, text: node.text })"
            >
              <!-- 四向拉伸手柄（放在容器内侧，不越界） -->
              <div class="resize-handle handle-top" @mousedown.stop="beginResize(node.id, 'top', $event)"></div>
              <div class="resize-handle handle-right" @mousedown.stop="beginResize(node.id, 'right', $event)"></div>
              <div class="resize-handle handle-bottom" @mousedown.stop="beginResize(node.id, 'bottom', $event)"></div>
              <div class="resize-handle handle-left" @mousedown.stop="beginResize(node.id, 'left', $event)"></div>

              <!-- 内层包裹：不再位移，避免与外层 transform 叠加 -->
              <div class="graph-node-inner">
                <div v-if="!editingNodeId || editingNodeId !== node.id">
                  {{ node.text }}
                </div>
                <el-input
                  v-else
                  v-model="editingNodeText"
                  class="node-edit-input"
                  @blur="confirmRename(node.id)"
                  @keyup.enter="confirmRename(node.id)"
                  @keyup.esc="cancelRename"
                  ref="editInputRef"
                />
                <table class="c-data-table">
                  <tr>
                    <th>名称</th>
                    <th>类型</th>
                  </tr>
                  <template v-for="column of node.data.columns" :key="column.name">
                    <tr>
                      <td>
                        <div :id="`${node.id}-${column.id}`">{{ column.name }}</div>
                      </td>
                      <td>{{ column.type }}</td>
                    </tr>
                  </template>
                </table>
              </div>
            </div>
          </template>
        </RelationGraph>

        <!-- 1. 节点右键菜单 -->
        <div v-show="showNodeContextMenu" :style="nodeContextMenuStyle" class="context-menu" @click.stop>
          <div class="menu-item" @click="handleDesignTable">设计表</div>
          <div class="menu-item" @click="handleRenameNode">重命名</div>
          <div class="menu-item" @click="handleDeleteNode" style="color: #ff4d4f;">删除表</div>
        </div>

        <!-- 2. 空白处右键菜单 -->
        <div v-show="showBlankContextMenu" :style="blankContextMenuStyle" class="context-menu" @click.stop>
          <div class="menu-item" @click="addNode">添加节点</div>
          <div class="menu-item" @click="clearGraph" v-if="isClear">清空图表</div>
        </div>
      </el-main>
      <el-footer>
        <el-button type="primary" @click="saveClick">保存</el-button>
      </el-footer>
    </el-container>
  </el-container>

  <DesignTableDialog
    :visible="showDesignDialog"
    :tableName="tableName"
    @cancel="cancel"
    @saveTableDesign="saveTableDesign"
  />
</template>

<script setup lang="ts">
import { onMounted, ref, nextTick, computed } from 'vue'
import RelationGraph from 'relation-graph-vue3'
import type { RelationGraphComponent, RGJsonData, RGNode } from 'relation-graph-vue3'
import { ElMessageBox, ElMessage } from 'element-plus'
import DesignTableDialog from '@/components/designTableDialog.vue'
import { useApp } from '@/store/app.ts'
import { useForeignKey } from '@/store/foreignKey.ts'
const useForeignKeyStore = useForeignKey()
import { useField } from '@/store/field.ts'
const useFieldStore = useField()
import { storeToRefs } from 'pinia'
const useAppStore = useApp()
const { treeData, lines, count } = storeToRefs(useAppStore)

const graphRef$ = ref<RelationGraphComponent | null>(null)
const graphData = ref<RGJsonData>({
  rootId: 'root',
  nodes: [],
  lines: [],
  elementLines:[]
})

// 限制数值在 [min, max] 范围
function clamp(v: number, min: number, max: number): number {
  return Math.min(Math.max(v, min), max)
}

// 刷新图（仅在必要时调用）
const refreshGraph = () => {
  const ls = graphData.value.lines
  useAppStore.setLines(ls)
  graphRef$.value?.setJsonData(graphData.value)
}

const options = {
  defaultExpandHolderPosition: 'right',
  allowNodeDrag: true,
  allowShowDownloadButton: false,
  layout: { layoutName: 'fixed' }
}

// 右键菜单
const showNodeContextMenu = ref(false)
const showBlankContextMenu = ref(false)
const nodeContextMenuStyle = ref({ left: '0px', top: '0px', position: 'fixed', zIndex: 1000 })
const blankContextMenuStyle = ref({ left: '0px', top: '0px', position: 'fixed', zIndex: 1000 })
// const currentNode = ref<RGNode | null>(null)
const currentNode = ref<any>(
  {
    "id": "node_1756396537077",
    "data": {
        "columns": [
            {
                "name": "id",
                "type": "int",
                "length": 0,
                "decimalPoint": 0,
                "isNull": "0",
                "virtual": "0",
                "key": "0",
                "annotation": "",
                "id": 1756396561837
            },
            {
                "name": "ids",
                "type": "varchar",
                "length": 255,
                "decimalPoint": 0,
                "isNull": "0",
                "virtual": "0",
                "key": "0",
                "annotation": "",
                "id": 1756396570326
            }
        ],
        "foreignKey": [
            {
                "fatherTable": "node_1756396535897",
                "field": 1756396561837,
                "fieldsReferenced": 1756396546754,
                "id": 1757945270152,
                "name": "11",
                "pattern": "Default"
            }
        ]
    },
    "text": "table_1"

})
const setContextMenuPosition = (styleRef: { value: any }, event: MouseEvent) => {
  styleRef.value = { ...styleRef.value, left: `${event.clientX}px`, top: `${event.clientY}px` }
}

// 节点尺寸管理
const DEFAULT_NODE_WIDTH = 150
const DEFAULT_NODE_HEIGHT = 200
type NodeSize = { width: number; height: number }
const nodeSizes = ref<Record<string, NodeSize>>({})

const getNodeSize = (nodeId: string) => {
  return nodeSizes.value[nodeId] || { width: DEFAULT_NODE_WIDTH, height: DEFAULT_NODE_HEIGHT }
}

const setNodeSize = (nodeId: string, width: number, height: number) => {
  const minWidth = 100,
    minHeight = 80,
    maxWidth = 600,
    maxHeight = 500
  nodeSizes.value[nodeId] = {
    width: Math.min(Math.max(width, minWidth), maxWidth),
    height: Math.min(Math.max(height, minHeight), maxHeight)
  }
}

// 容器仅负责尺寸
const getNodeInlineStyle = (nodeId: string) => {
  const size = getNodeSize(nodeId)
  return {
    width: `${size.width}px`,
    height: `${size.height}px`,
    backgroundColor: '#3783c0',
    position: 'relative',
    boxSizing: 'border-box'
  } as any
}

// 拖拽状态
type ResizeEdge = 'top' | 'right' | 'bottom' | 'left'
const resizing = ref<null | {
  nodeId: string
  edge: ResizeEdge
  startX: number
  startY: number
  startWidth: number
  startHeight: number
  nodeStartX: number
  nodeStartY: number
  outerEl: HTMLElement | null
  baseTransform: string
}>(null)

// 查找 RelationGraph 外层节点容器（包含高亮环的那个）
function findOuterNodeEl(container: HTMLElement | null): HTMLElement | null {
  if (!container) return null
  let el: HTMLElement | null = container
  while (el && el !== document.body) {
    if (el.classList.contains('rel-node')) return el
    el = el.parentElement as HTMLElement | null
  }
  // 兜底：模糊匹配包含 rel-node 文本的类名
  return (container.closest('[class*="rel-node"]') as HTMLElement) || null
}

const onResizingMouseMove = (e: MouseEvent) => {
  const st = resizing.value
  if (!st) return
  const { nodeId, edge, startX, startY, startWidth, startHeight, outerEl, baseTransform } = st

  const dx = e.clientX - startX
  const dy = e.clientY - startY
  const minW = 100,
    maxW = 600
  const minH = 80,
    maxH = 500

  if (!outerEl) return

  if (edge === 'right') {
    const newW = clamp(startWidth + dx, minW, maxW)
    setNodeSize(nodeId, newW, startHeight)

    // 同步外层尺寸（不位移）
    outerEl.style.width = newW + 'px'
    outerEl.style.height = startHeight + 'px'
    outerEl.style.transform = baseTransform
    return
  }

  if (edge === 'bottom') {
    const newH = clamp(startHeight + dy, minH, maxH)
    setNodeSize(nodeId, startWidth, newH)

    outerEl.style.width = startWidth + 'px'
    outerEl.style.height = newH + 'px'
    outerEl.style.transform = baseTransform
    return
  }

  if (edge === 'left') {
    let tentativeW = clamp(startWidth - dx, minW, maxW)
    const appliedDx = startWidth - tentativeW // 固定右边需要右移的量
    setNodeSize(nodeId, tentativeW, startHeight)

    outerEl.style.width = tentativeW + 'px'
    outerEl.style.height = startHeight + 'px'
    outerEl.style.transform = `${baseTransform} translate(${appliedDx}px, 0)`
    return
  }

  if (edge === 'top') {
    let tentativeH = clamp(startHeight - dy, minH, maxH)
    const appliedDy = startHeight - tentativeH // 固定下边需要下移的量
    setNodeSize(nodeId, startWidth, tentativeH)

    outerEl.style.width = startWidth + 'px'
    outerEl.style.height = tentativeH + 'px'
    outerEl.style.transform = `${baseTransform} translate(0, ${appliedDy}px)`
    return
  }
}

const onResizingMouseUp = () => {
  const st = resizing.value
  if (!st) return
  const { nodeId, nodeStartX, nodeStartY, edge, startWidth, startHeight, outerEl } = st

  let dxCommit = 0,
    dyCommit = 0
  const size = getNodeSize(nodeId)
  if (edge === 'left') dxCommit = startWidth - size.width
  if (edge === 'top') dyCommit = startHeight - size.height

  // 提交坐标（沉淀临时位移）
  const idx = graphData.value.nodes.findIndex(n => n.id === nodeId)
  if (idx > -1) {
    graphData.value.nodes[idx].x = nodeStartX + dxCommit
    graphData.value.nodes[idx].y = nodeStartY + dyCommit
  }

  // 清理外层临时 transform（回归数据驱动）
  if (outerEl) {
    outerEl.style.transform = ''
  }

  refreshGraph()

  document.removeEventListener('mousemove', onResizingMouseMove)
  document.removeEventListener('mouseup', onResizingMouseUp)
  resizing.value = null
}

const beginResize = (nodeId: string, edge: ResizeEdge, e: MouseEvent) => {
  e.preventDefault()
  e.stopPropagation()

  const size = getNodeSize(nodeId)
  const node = graphData.value.nodes.find(n => n.id === nodeId)!

  const container = (e.currentTarget as HTMLElement).closest('.graph-node-container') as HTMLElement | null
  const outerEl = findOuterNodeEl(container)
  let baseTransform = ''
  if (outerEl) {
    const cs = getComputedStyle(outerEl)
    baseTransform = cs.transform === 'none' ? '' : cs.transform
    // 初次记录时，确保外层尺寸与内部容器一致
    outerEl.style.width = size.width + 'px'
    outerEl.style.height = size.height + 'px'
  }

  resizing.value = {
    nodeId,
    edge,
    startX: e.clientX,
    startY: e.clientY,
    startWidth: size.width,
    startHeight: size.height,
    nodeStartX: node.x ?? 0,
    nodeStartY: node.y ?? 0,
    outerEl,
    baseTransform
  }

  document.addEventListener('mousemove', onResizingMouseMove)
  document.addEventListener('mouseup', onResizingMouseUp)
}

// 重命名/表设计等逻辑
const editingNodeId = ref<string | null>(null)
const editingNodeText = ref('')
const editInputRef = ref<HTMLInputElement | null>(null)

const showDesignDialog = ref(false)
const tableName = ref('')

const isClear = computed(() => graphData.value.nodes.length > 0)

onMounted(() => {
  if (treeData.value[0].children && treeData.value[0].children.length > 0) {
    graphData.value.nodes = treeData.value[0].children.map((it: any) => it.otherData)
    // if (lines.value.length > 0) {
    //   graphData.value.lines = lines.value
    // }
  }
  refreshGraph()
  setTimeout(()=>{
    saveTableDesign({"field": [
        {
            "name": "id",
            "type": "int",
            "length": 0,
            "decimalPoint": 0,
            "isNull": "0",
            "virtual": "0",
            "key": "0",
            "annotation": "",
            "id": 1756396561837
        },
        {
            "name": "ids",
            "type": "varchar",
            "length": 255,
            "decimalPoint": 0,
            "isNull": "0",
            "virtual": "0",
            "key": "0",
            "annotation": "",
            "id": 1756396570326
        }
    ],
    "foreignKey":[
        {
            "fatherTable": "node_1756396535897",
            "field": 1756396561837,
            "fieldsReferenced": 1756396546754,
            "id": 1757945270152,
            "name": "11",
            "pattern": "Default"
        }
    ]})
  },300)
  
})

const handleRightClick = (event: MouseEvent, node?: RGNode) => {
  event.preventDefault()
  event.stopPropagation()
  closeAllContextMenu()

  const isClickNode =
    event.target instanceof HTMLElement && (event.target.closest('.graph-node-container') as HTMLElement | null)

  if (isClickNode) {
    try {
      const dataAttr = (isClickNode as HTMLElement).getAttribute('data-node')
      currentNode.value = dataAttr ? JSON.parse(dataAttr) : node || null
    } catch (_) {
      currentNode.value = node || null
    }
    if (currentNode.value) {
      showNodeContextMenu.value = true
      setContextMenuPosition(nodeContextMenuStyle as any, event)
    } else {
      showBlankContextMenu.value = true
      setContextMenuPosition(blankContextMenuStyle as any, event)
    }
  } else {
    showBlankContextMenu.value = true
    setContextMenuPosition(blankContextMenuStyle as any, event)
  }

  const closeMenu = () => {
    closeAllContextMenu()
    document.removeEventListener('click', closeMenu)
  }
  setTimeout(() => document.addEventListener('click', closeMenu), 0)
}

const treeCurrentChange = (node: any) => {
  useAppStore.setNodeShape(node.id)
  if (treeData.value[0].children && treeData.value[0].children.length > 0) {
    graphData.value.nodes = treeData.value[0].children.map((it: any) => it.otherData)
    if (lines.value.length > 0) {
      graphData.value.lines = lines.value
    }
  }
  refreshGraph()
}

const handleNodeClick = (node: RGNode) => {
  treeCurrentChange(node)
}

const customNodeClass = (node: any) => (node?.otherData?.nodeShape == 1 ? 'is-nodeShape' : null)

const closeAllContextMenu = () => {
  showNodeContextMenu.value = false
  showBlankContextMenu.value = false
}

const saveClick = () => {
  console.log('saveClick', graphData.value)
}

const addNode = () => {
  if (!graphRef$.value) return
  const newNodeId = `node_${Date.now()}`
  const left = parseInt(blankContextMenuStyle.value.left) || 0
  const top = parseInt(blankContextMenuStyle.value.top) || 0
  const temp = {
    id: newNodeId,
    text: `table_${count.value}`,
    x: left + 20,
    y: top + 20,
    color: '#3783c0',
    data: { columns: [], foreignKey: [] }
  }
  graphData.value.nodes.push({ ...temp, nodeShape: 1 })
  useAppStore.add({ label: temp.text, id: temp.id, otherData: { ...temp, nodeShape: 1 } })
  useAppStore.setCount()
  refreshGraph()
  closeAllContextMenu()
}

const clearGraph = () => {
  if (!graphRef$.value || !confirm('确定要清空图表吗？')) return
  graphData.value.nodes = []
  graphData.value.lines = []
  refreshGraph()
  useAppStore.clear()
  closeAllContextMenu()
}

const handleDesignTable = () => {
  if (!currentNode.value) return
  const node = currentNode.value
  tableName.value = node.text!
  useFieldStore.setData(node.data!.columns.length ? node.data!.columns : [])
  useForeignKeyStore.setData(node.data!.foreignKey.length ? node.data!.foreignKey : [])
  showDesignDialog.value = true
  closeAllContextMenu()
}

const handleRenameNode = () => {
  if (!currentNode.value) return
  const node = currentNode.value
  editingNodeId.value = node.id
  editingNodeText.value = node.text!
  nextTick(() => editInputRef.value?.focus())
  closeAllContextMenu()
}

const confirmRename = (nodeId: string) => {
  const nodeIndex = graphData.value.nodes.findIndex(n => n.id === nodeId)
  if (nodeIndex > -1 && editingNodeText.value.trim()) {
    graphData.value.nodes[nodeIndex].text = editingNodeText.value.trim()
    refreshGraph()
  }
  useAppStore.editName(nodeId, editingNodeText.value.trim())
  editingNodeId.value = null
}

const cancelRename = () => {
  editingNodeId.value = null
}

const handleDeleteNode = () => {
  ElMessageBox.confirm('确定要删除吗', '提示', { confirmButtonText: '确定', cancelButtonText: '取消', type: 'warning' })
    .then(() => {
      const nodeId = currentNode.value!.id
      graphData.value.nodes = graphData.value.nodes.filter(n => n.id !== nodeId)
      graphData.value.lines = graphData.value.lines.filter(line => line.from !== nodeId && line.to !== nodeId)
      refreshGraph()
      useAppStore.del(nodeId)
      closeAllContextMenu()
      currentNode.value = null
    })
    .catch(() => {
      ElMessage.info('你取消了删除')
    })
}

const cancel = () => {
  showDesignDialog.value = false
  useForeignKeyStore.clear()
  useFieldStore.clear()
}

const saveTableDesign = ({ field, foreignKey }: { field: any[]; foreignKey: any[] }) => {
  console.log('saveTableDesign', field, foreignKey)
  // 1) 基本校验
  if (!currentNode.value) return
  const nodeId = currentNode.value.id
  const node = graphData.value.nodes.find(n => n.id === nodeId)
  if (!node) return

  // 2) 更新节点数据（防御式拷贝）
  const safeFields = Array.isArray(field) ? [...field] : []
  const safeForeignKeys = Array.isArray(foreignKey) ? [...foreignKey] : []
  node.data = node.data || { columns: [], foreignKey: [] }
  node.data.columns = safeFields
  node.data.foreignKey = safeForeignKeys

  // 3) 清理该节点原有的元素级连线，避免重复与脏数据
  const prefixFrom = `${nodeId}-`
  graphData.value.elementLines = (graphData.value.elementLines || []).filter(line => !String(line.from || '').startsWith(prefixFrom))

  // 4) 构建新的元素级连线（去重、过滤无效项）
  const uniqueKeySet = new Set<string>()
  for (const fk of safeForeignKeys) {
    const hasFather = fk && typeof fk.fatherTable === 'string' && fk.fatherTable.trim()
    const hasField = fk && (fk.field !== undefined && fk.field !== null)
    const hasRef = fk && (fk.fieldsReferenced !== undefined && fk.fieldsReferenced !== null)
    if (!hasFather || !hasField || !hasRef) continue

    const from = `${nodeId}-${fk.field}`
    const to = `${fk.fatherTable}-${fk.fieldsReferenced}`
    const key = `${from}|${to}`
    if (uniqueKeySet.has(key)) continue
    uniqueKeySet.add(key)

    graphData.value.elementLines.push({
      from,
      to,
      color: '#ffd700',
      lineWidth: 3
    })
  }

  // 5) 刷新图
  refreshGraph()
  // useAppStore.setOtherData(nodeId, { field, foreignKey })
  // cancel()
}
</script>

<style lang="scss" scoped>
.el-container {
  height: 100%;
}

:deep(.el-tree) {
  .is-nodeShape {
    background-color: #f00;
  }
}

.el-footer {
  display: flex;
  justify-content: flex-end;
}

/* 关系图基础样式 */
::v-deep(.relation-graph) {
  .rel-node-checked {
    box-shadow: none;
  }

  .graph-node-container {
    overflow: hidden;
  }

  .is-nodeShape {
    box-shadow: 0 0 0 8px #ff00004d;
  }

  .rel-map {
    background-color: rgba(255, 0, 0, 0.1);
    background-size: 30px 30px;
    background-image: linear-gradient(90deg, rgba(0, 0, 0, 0.1) 1px, transparent 0),
      linear-gradient(180deg, rgba(0, 0, 0, 0.1) 1px, transparent 0);
  }
}

.el-main {
  padding: 0;
}

/* 右键菜单 */
.context-menu {
  width: 120px;
  background: white;
  border-radius: 4px;
  box-shadow: 0 2px 10px rgba(0, 0, 0, 0.15);
  border: 1px solid #e0e0e0;
  padding: 5px 0;

  .menu-item {
    padding: 8px 15px;
    font-size: 14px;
    cursor: pointer;
    transition: background 0.2s;

    &:hover {
      background-color: #f5f5f5;
      color: #1890ff;
    }
  }
}

/* 节点表格样式 */
.c-data-table {
  background-color: #ffffff;
  border-collapse: collapse;
  width: 100%;
  margin-top: 10px;

  td,
  th {
    border: 1px solid #3783c0;
    color: #333333;
    padding: 5px;
    text-align: center;
  }

  td div {
    background-color: #1da9f5;
    color: #ffffff;
    border-radius: 5px;
    padding: 2px 0;
  }
}

/* 节点重命名输入框样式 */
.node-edit-input {
  width: 100%;
  padding: 4px 8px;
  border: 1px solid #1da9f5;
  border-radius: 4px;
  outline: none;
  font-size: 14px;
}

/* 节点容器与内层 */
.graph-node-container {
  overflow: hidden;
  position: relative;
  box-sizing: border-box;
}

.graph-node-inner {
  position: absolute;
  inset: 0;
}

/* 拉伸手柄样式（贴内缘，不越界） */
.resize-handle {
  position: absolute;
  background: transparent;
  z-index: 2;
}

.handle-top {
  top: 0;
  left: 0;
  right: 0;
  height: 6px;
  cursor: ns-resize;
}

.handle-right {
  top: 0;
  right: 0;
  width: 6px;
  bottom: 0;
  cursor: ew-resize;
}

.handle-bottom {
  bottom: 0;
  left: 0;
  right: 0;
  height: 6px;
  cursor: ns-resize;
}

.handle-left {
  top: 0;
  left: 0;
  width: 6px;
  bottom: 0;
  cursor: ew-resize;
}
</style>